package com.qf.commons.mysql.plugin;

import com.qf.commons.mysql.utils.MybatisUtils;
import com.qf.commons.mysql.utils.QphonePage;
import com.qf.data.page.Page;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * description: 自定义的MyBatis的分页插件
 * author: Ken
 * 公众号：Java架构栈
 */
@Intercepts(
        @Signature(
                type = StatementHandler.class,
                method = "prepare",
                args = {Connection.class, Integer.class}
        )
)
@Slf4j
public class PagePlugin implements Interceptor {

    /**
     * 拦截的是MyBatis的Statement准备的方法
     * @param invocation
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        //获取StatmentHandler
        StatementHandler statementHandler = (StatementHandler) MybatisUtils.getNoProxyObject(invocation.getTarget());
        BoundSql boundSql = statementHandler.getBoundSql();
        //获取拦截的SQL语句
        String sql = statementHandler.getBoundSql().getSql().toLowerCase().replaceAll("\n", "");

        //判断当前是否为查询的sql
        if (!sql.startsWith("select")) {
            //非查询语句
            //直接放行
            return invocation.proceed();
        }

        //当前是select语句
        //根据当前的请求上下文，获取Page分页对象，如果能获取到，说明需要分页，如果不能需获取，说明无需分页
        //ThreadLocal  A线程 -> ThreadLocal对象 -> A线程
        Page page = QphonePage.getPage();
        if (page == null) {
            //未获取到分页对象
            return invocation.proceed();
        }

        //开始分页 - select * from student
        log.debug("[page - plugin] 开始分页 - {}", sql);
        //计算出总条数
        Integer count = getCount(sql, invocation, statementHandler);
        //进行相关的参数设置
        if (page.getPageSize() == null) page.setPageSize(10);//默认每页显示10条
        page.setPageCount(count);//设置总条数
        page.setPageTotal(page.getPageCount() % page.getPageSize() == 0 ?
                page.getPageCount() / page.getPageSize() :
                page.getPageCount() / page.getPageSize() + 1);//设置总页码
        //判断页码的临界值
        if (page.getPageNum() < 1) page.setPageNum(1);
        if (page.getPageNum() > page.getPageTotal() && page.getPageTotal() > 0) page.setPageNum(page.getPageTotal());

        //开始进行分页
        //策略模式 - 根据不用的数据库，选择不用的sql分页模式
        sql += " limit " + ((page.getPageNum() - 1) * page.getPageSize()) + "," + page.getPageSize();
        log.debug("[page - plugin] 生成分页sql - {}", sql);

        //开始执行分页sql
        //将修改后的sql，替换掉原来的sql，放行
        MetaObject metaObject = SystemMetaObject.forObject(boundSql);
        metaObject.setValue("sql", sql);

        log.debug("[page - plugin] 分页完成！");

        //放行，让Mybatis执行当前修改后的SQL语句
        return invocation.proceed();
    }

    /**
     * 计算查询的总条数
     * @return
     */
    private Integer getCount(String sql, Invocation invocation, StatementHandler statementHandler) throws SQLException {
        //计算总条数的sql语句
        String countSql = "select count(*) as count " + sql.substring(MybatisUtils.getMasterFormIndex(0, sql));
        log.debug("[page - plugin] 生成记录总条数的sql语句 - {}", countSql);

        //执行sql语句
        //获取相关参数
        Connection connection = (Connection) invocation.getArgs()[0];
        //编写jdbc执行总条数的sql
        PreparedStatement ps = null;
        ResultSet resultSet = null;
        try {
            ps = connection.prepareStatement(countSql);
            //设置SQL的相关参数 - 借助Mybatis本身的机制，设置查询参数
            statementHandler.parameterize(ps);
            //执行sql
            resultSet = ps.executeQuery();
            //获取结果
            if (resultSet.next()) {
                int count = resultSet.getInt("count");
                log.debug("[page - plugin] 获取查询的记录总条数 - {}", count);
                return count;
            }
        } catch (SQLException e) {
            throw e;
        } finally {
            if (resultSet != null) {
                resultSet.close();
            }

            if (ps != null) {
                ps.close();
            }
        }
        return 0;
    }
}
